﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using SouthApps.MobileHealth.SDK.DAL.Model;
using SouthApps.MobileTasks.SDK.DAL.Repositories;
using SouthApps.Framework.LoggerService;

namespace SouthApps.MobileHealth.SDK.Cache
{
    public static class SucursalesPrestadorCache
    {
        private static ReaderWriterLock cacheReaderWriterLock = new ReaderWriterLock();
        private static IPrestadoresRepository prestadoresRepository;
        private static Dictionary<int, List<SucursalPrestador>> sucursalesPrestadorFinanciador = new Dictionary<int, List<SucursalPrestador>>();
        private static bool initialized = false;
        private static object lockObject = new object();

        public static void Initialize(List<int> financiadoresId)
        {
            try
            {
                lock (lockObject)
                {
                    if (!initialized)
                    {
                        SucursalesPrestadorCache.prestadoresRepository = new PrestadoresRepository();
                        SucursalesPrestadorCache.LoadSucursalesPrestadorFromDatabase(financiadoresId);
                        initialized = true;
                    }
                }
            }
            catch (Exception ex)
            {
                LoggerService.Log(LogLevel.Error, "An error ocurred while initializing sucursales prestador cache.", ex);
                throw ex;
            }
        }

        private static void LoadSucursalesPrestadorFromDatabase(List<int> financiadoresId)
        {
            cacheReaderWriterLock.AcquireWriterLock(Timeout.Infinite);

            try
            {
                foreach (int financiadorId in financiadoresId)
                {
                    List<SucursalPrestador> sucursalPrestador = SucursalesPrestadorCache.prestadoresRepository.GetSucursalesPrestadorFinanciador(financiadorId);
                    SucursalesPrestadorCache.sucursalesPrestadorFinanciador.Add(financiadorId, sucursalPrestador);
                }
            }
            finally
            {
                cacheReaderWriterLock.ReleaseWriterLock();
            }
        }

        public static void AddSucursalPrestadorToCache(int financiadorId, SucursalPrestador sucursalPrestador)
        {
            cacheReaderWriterLock.AcquireReaderLock(Timeout.Infinite);

            if (SucursalesPrestadorCache.sucursalesPrestadorFinanciador.ContainsKey(financiadorId))
            {
                List<SucursalPrestador> sucursalesPrestadorList = SucursalesPrestadorCache.sucursalesPrestadorFinanciador[financiadorId];

                cacheReaderWriterLock.UpgradeToWriterLock(Timeout.Infinite);
                sucursalesPrestadorList.Add(sucursalPrestador);
                SucursalesPrestadorCache.sucursalesPrestadorFinanciador[financiadorId] = sucursalesPrestadorList;

                cacheReaderWriterLock.ReleaseLock();
            }
            else
            {
                List<SucursalPrestador> sucursalesPrestadorList = new List<SucursalPrestador>();
                sucursalesPrestadorList.Add(sucursalPrestador);

                cacheReaderWriterLock.UpgradeToWriterLock(Timeout.Infinite);
                SucursalesPrestadorCache.sucursalesPrestadorFinanciador.Add(financiadorId, sucursalesPrestadorList);

                cacheReaderWriterLock.ReleaseLock();
            }
        }

        public static void RemoveSucursalPrestadorFromCache(int financiadorId, SucursalPrestador sucursalPrestador)
        {
            cacheReaderWriterLock.AcquireReaderLock(Timeout.Infinite);

            try
            {
                if (!SucursalesPrestadorCache.sucursalesPrestadorFinanciador.ContainsKey(financiadorId))
                {
                    return;
                }

                List<SucursalPrestador> sucursalesPrestadorList = SucursalesPrestadorCache.sucursalesPrestadorFinanciador[financiadorId];

                cacheReaderWriterLock.UpgradeToWriterLock(Timeout.Infinite);
                sucursalesPrestadorList.Remove(sucursalPrestador);
                SucursalesPrestadorCache.sucursalesPrestadorFinanciador[financiadorId] = sucursalesPrestadorList;
            }
            finally
            {
                cacheReaderWriterLock.ReleaseLock();
            }
        }

        public static void UpdateSucursalPrestadorToCache(int financiadorId, SucursalPrestador sucursalPrestador)
        {
            cacheReaderWriterLock.AcquireReaderLock(Timeout.Infinite);

            try
            {
                if (!SucursalesPrestadorCache.sucursalesPrestadorFinanciador.ContainsKey(financiadorId))
                {
                    return;
                }

                List<SucursalPrestador> sucursalesPrestadorList = SucursalesPrestadorCache.sucursalesPrestadorFinanciador[financiadorId];
                //TODO: terminar
            }
            finally
            {
                cacheReaderWriterLock.ReleaseLock();
            }
        }

        public static List<SucursalPrestador> GetSucursalesPrestador(int financiadorId, int especialidadId, int planId)
        {
            return SucursalesPrestadorCache.GetSucursalesPrestador(financiadorId, especialidadId, planId, null);
        }

        public static List<SucursalPrestador> GetSucursalesPrestador(int financiadorId, int especialidadId, int planId, int? departamentoId)
        {
            cacheReaderWriterLock.AcquireReaderLock(Timeout.Infinite);

            try
            {
                List<SucursalPrestador> sucursalesPrestador = new List<SucursalPrestador>();

                SucursalesPrestadorCache.sucursalesPrestadorFinanciador.TryGetValue(financiadorId, out sucursalesPrestador);

                var sucursalesPorUbicacion = from sucursalPrestador in sucursalesPrestador
                                             where (departamentoId == null || sucursalPrestador.departamentoId == departamentoId)
                                             select sucursalPrestador;

                var sucursalesPorUbicacionEnConvenio = from sucursalPrestador in sucursalesPorUbicacion
                                                       where sucursalPrestador.convenio.Any(c => c.especialidadId == especialidadId && c.planId == planId)
                                                       select sucursalPrestador;

                return sucursalesPorUbicacionEnConvenio.ToList();
            }
            finally
            {
                cacheReaderWriterLock.ReleaseReaderLock();
            }
        }
    }
}
